//----------------------------------------------------------------------------------------------------------------------
/// \file
/// <summary>Waiter classes for Etw events.</summary>
// Copyright (c) Microsoft Corporation.  All Rights Reserved.
//----------------------------------------------------------------------------------------------------------------------

#pragma once
#include "EtwProcessor.h"

#include <windows.h>

// Forward define VARIANT struct
typedef struct tagVARIANT VARIANT;

typedef struct _EVENT_RECORD EVENT_RECORD;

/// \namespace Etw::Processor
/// <summary>
/// The Etw::Processor namespace contains Etw waiter class declarations.
/// </summary>
namespace Etw { namespace Processor
{
    /// <summary>
    /// Base class for all Waiter classes.
    /// </summary>
    class ETWPROCESSOR_API Waiter
    {
        friend class CachingWaiter;
        friend class EtwWaiter;
        friend class MultiplicityWaiter;
        friend class WaiterCollection;

    public:
        virtual ~Waiter();
        Waiter(const Waiter& other);
        Waiter& operator=(Waiter other);

        /// <summary>Method that blocks the current thread until the required event is received</summary>
        /// <param name="timeout">Time in milliseconds to wait for</param>
        /// <returns>S_OK if received the event</returns>
        virtual HRESULT Wait(unsigned int timeout);
        /// <summary>Method that blocks the current thread until the required event is received</summary>
        /// <returns>S_OK if received the event</returns>
        virtual HRESULT Wait();
        /// <summary>Method to reset the current waiter</summary>
        virtual void Reset();

        HRESULT GetStatus() const;

    protected:
        Waiter();

        struct Impl;
        Impl* m_pImpl;
        HRESULT m_status; // hr stored if any exception encountered
    };

    /// <summary>
    /// Light weight waiter to wait for an Etw event
    /// </summary>
    class ETWPROCESSOR_API EtwWaiter : public Waiter
    {
    public:
        /// <summary>Construct an EtwWaiter</summary>
        /// <param name="providerGuid">Guid of the provider that raises events</param>
        /// <param name="eventId">Event Identifier</param>
        EtwWaiter(GUID providerGuid, unsigned long eventId);
        /// <summary>Construct an EtwWaiter</summary>
        /// <param name="providerGuid">Guid of the provider that raises events</param>
        /// <param name="eventId">Event Identifier</param>
        /// <param name="pszPayloadCriteria">String representing payload to filter the event</param>
        EtwWaiter(GUID providerGuid, unsigned long eventId, const wchar_t* pszPayloadCriteria);

#ifdef ETWPROCESSOR_FULL_BUILD
        EtwWaiter(GUID providerGuid, unsigned long eventId, const __wchar_t* pszPayloadCriteria);
#endif

        ~EtwWaiter();

    private:
        void Initialize(GUID providerGuid, unsigned long eventId, const wchar_t* pszPayloadCriteria);
    };

    /// <summary>
    /// Class that represents an etw event
    /// </summary>
    class ETWPROCESSOR_API EtwEvent
    {
        friend class CachingWaiter;

    public:
        static EtwEvent __stdcall FromEventRecord(EVENT_RECORD* pEventRecord);

        ~EtwEvent();
        EtwEvent(const EtwEvent& other);
        EtwEvent& operator=(EtwEvent other);

        HRESULT GetStatus() const;

        /// <summary>Method to get the event identifier</summary>
        unsigned long GetEventId();
        /// <summary>Method to get the event version</summary>
        unsigned char GetVersion();
        /// <summary>Method to get the event Channel</summary>
        unsigned char GetChannel();
        /// <summary>Method to get the event Level</summary>
        unsigned char GetLevel();
        /// <summary>Method to get the event OpCode</summary>
        unsigned char GetOpCode();
        /// <summary>Method to get the event thred Identifier</summary>
        unsigned long GetThreadId();
        /// <summary>Method to get the event Process Identifier</summary>
        unsigned long GetProcessId();
        /// <summary>Method to get the event provider Guid</summary>
        GUID GetProviderGuid();
        /// <summary>Method to get the event timestamp</summary>
        LARGE_INTEGER GetTimeStamp();

        const unsigned short* GetProviderName() const;
        const unsigned short* GetLevelName() const;
        const unsigned short* GetChannelName() const;
        const unsigned short* GetKeywordsName() const;
        const unsigned short* GetTaskName() const;
        const unsigned short* GetOpCodeName() const;
        const unsigned short* GetEventMessage() const;
        const unsigned short* GetProviderMessage() const;

        /// <summary>Method that prints the event properties to the console</summary>
        void PrintProperties();
        /// <summary>Method to retrieve payload property value of the event</summary>
        /// <param name="pszPropertyName">Name of the property to retrieve</param>
        /// <param name="variant">VARIANT structure that holds the value of the property</param>
        HRESULT GetPayloadProperty(const wchar_t* pszPropertyName, VARIANT& variant);

        #ifdef ETWPROCESSOR_FULL_BUILD
        HRESULT GetPayloadProperty(const __wchar_t* pszPropertyName, VARIANT& variant);
        #endif

    private:
        EtwEvent();

        struct Impl;
        Impl* m_pImpl;
        HRESULT m_status;
    };

    /// <summary>
    /// Waiter that also caches the event that is waiting for
    /// </summary>
    class ETWPROCESSOR_API CachingWaiter : public Waiter
    {
    public:
        /// <summary>Constructor for CachingWaiter</summary>
        /// <param name="waiter">Waiter object</param>
        CachingWaiter(Waiter waiter);
        /// <summary>Constructor for CachingWaiter</summary>
        /// <param name="waiter">Waiter object</param>
        /// <param name="pszPayloadCriteria">String representing payload to filter the event</param>
        CachingWaiter(Waiter waiter, const wchar_t* pszPayloadCriteria);
        
        #ifdef ETWPROCESSOR_FULL_BUILD
        CachingWaiter(Waiter waiter, const __wchar_t* pszPayloadCriteria);
        #endif
        
        ~CachingWaiter()
        {
        }

        /// <summary>Method to get the Cached event</summary>
        EtwEvent GetCachedEvent();
    private:
        void Initialize(Waiter& waiter, const wchar_t* pszPayloadCriteria);
    };


    /// <summary>
    /// Class that represents a collection of Etw waiter classes
    /// </summary>
    class ETWPROCESSOR_API WaiterCollection
    {
        friend class CompositeWaiter;
    public:
        WaiterCollection();
        ~WaiterCollection();

        WaiterCollection(const WaiterCollection& other);
        WaiterCollection& operator=(WaiterCollection other);

        HRESULT GetStatus() const;

        /// <summary>Method to add a waiter to the collection</summary>
        /// <param name="waiter">Waiter object</param>
        void Add(Waiter waiter);

    protected:
        struct Impl;
        Impl* m_pImpl;
        HRESULT m_status;
    };

    /// <summary>
    /// Waiter to wait for multiple instances of a same event
    /// </summary>
    class ETWPROCESSOR_API MultiplicityWaiter : public Waiter
    {
    public:
        /// <summary>Constructor for MultiplicityWaiter</summary>
        /// <param name="waiter">Waiter object</param>
        /// <param name="instances">Number of instances of the Event</param>
        MultiplicityWaiter(Waiter waiter, unsigned int instances);
        ~MultiplicityWaiter()
        {
        }
    };

    /// <summary>
    /// Waiter to WaitForAny or WaitForAll events from multiple providers at the same time
    /// </summary>
    class ETWPROCESSOR_API CompositeWaiter : public Waiter
    {
    public:
        CompositeWaiter(WaiterCollection waiterCollection);
        ~CompositeWaiter()
        {
        }

        HRESULT WaitAny(unsigned int timeout);
        HRESULT WaitAny();
        
        HRESULT WaitAll(unsigned int timeout);
        HRESULT WaitAll();
    };

}/* namespace Processor */ } /* namespace Etw */
